承接昨天講解傳址和傳值的文章,今天再來看看一些相關的例子:
[] == [] //false
[] == ![] //true
{} == {} //false
{} == !{} //false
事緣在練習BMI計算器的時候,這個網頁要用到localStorage去儲存使用者的BMI計算結果並顯示在網頁上。我打算在顯示在網頁上的每項記錄旁,都有一個刪除的按鈕給使用者刪除該項記錄,所以把click事件連到splice這個語法。如果使用者把所有記錄全都刪除掉時,我就想在內容塞入「請輸入體重和身高」這段字。
if(data == []){
list.innerHTML = `<p class="instruction">請輸入體重和身高</p>`
}
但寫完之後卻沒有得出我要的結果,它並沒有塞那段文字。所以我就去console查data現在是什麼,之後查到底data == []這個判斷式是不是true,這時候才發現原來不是。

雖然我轉用以下的方法就行,成功蒙混過關:
if(data.length == 0){
list.innerHTML = `<p class="instruction">請輸入體重和身高</p>`
}
但踩過的坑還是要老老實實地去學習XD,所以就去google和請教大大,然後發現這個問題也牽涉到不少經典的坑,於是就借這篇文章好好整理自己的理解。
看了一些網上文章,不少人都在引用JavaScript 高级程序設計(第3版)中提及的規則,令我也不禁去翻翻原文:
Both operators do conversions to determine if two operands are equal (often called type coercion).
首先,作者有提及到type coercion這個概念,他指出在判斷相等性時,會先進行型別轉換,才去決定兩邊的運算數(operand)是否對等。
以下是作者列舉的規則:


我自己的理解:
false會轉為0,true會轉為1。valueOf()去取得物件的基本類型值,並按剛才提及的規則做比較。null和undefined是相等的。null和undefined是不能被轉型為其他值來去檢查是否相等。NaN,相等操作符會回傳false,不相等操作符會回傳true。注意:即使兩個運算數都是NaN,相等操作符都會回傳false,因為按規則,NaN不會等於NaN
true,否則回傳false。再看看MDN的簡潔圖解:

金魚腦忘了物件是什麼?先重溫一下資料型別:
number、string、boolean、undefined、null、symbol(ES6加入)。它的值叫基本類型值(Prmitive value)。object、array、function等等。它的值叫引用值(Reference value)。注意:基本類型值和引用值存放在記憶體的做法是不同的。如果它是基本值,就會單純被存放到棧內存(stack),如果是引用值,它的值會存放到堆內存(heap),並在棧內存(stack)裏存放它在堆內存(heap)的地址。這個重點在上文有提及:

當一個變數被賦予一個值的時候,直釋器會基於它的資料型別,檢查它是基本還是引用值,再把該值存放到某個記憶體位置。
[] == []會回傳false。
根據剛才提及的第7點,如果兩個運算數是物件(陣列是物件),就會比較它們是否屬於同一個物件。但因為它們的地址不同,所以它們並非同一個物件。
雖然這裏的兩個[]都是空陣列,但其實它們被存放到不同的記憶體位置裏,兩者各有不同的地址。如果要作比較,因為它們的地址不同,指向的物件不同,所以會回傳false。

而{} == {}都會回傳false,這個問題的原因與剛才提到[] == []回傳false是一樣的。
好像懂了?但再看看這個例子,為什麼會回傳true呢?
這裏會回傳true是因為testB拷貝了testA引用值的地址,而它們的地址都是指向同一個引用值。
如果修改了testB,testA都會被修改,因為它們的地址是一樣,指向的物件也一樣,所以兩者都會被改掉。


另外一個常見的問題就是[]==![],答案是回傳true。
初學JS的我,經常會用到!==或!===,但就很少把!放在值的前面。之後我Google了一下它的用法,原來除了!,還有!!:
!的意思:
true(truthy)還是false(falsy)例如0的布林值是false,!0就會回傳true。1的布林值是true,!1就會回傳false。
!!的意思,就是相反再相反:
true(truthy)還是false(falsy),把它相反例如!!0會回傳false,!!1會回傳true,把該值老老實實地轉回它應有的布林值。
不過,!!並不是另一個運算符,只是重複打多個!而己。
有興趣了解!可看看這篇
廢話講多了,回到正題,這條問題的解答很易理解:
! 的優先級比等號==高,所以會先把 ![] 轉成布林值,而它的值會是false 。[]==false ,根據上面的7點規則中第1點,false會是等於0,所以會變成[]==0。[]轉成基本值,用([]).toString()轉成"",把""轉成數字是0,所以是true。也可以參考MDN的圖:
參考運算符優先級
雖然剛才[] == ![]是true,但{} == !{}會回傳false。
這條問題的邏輯與剛才提及的非常相似:
!{}轉為布林值,即是false
false轉為0,程式碼會變成{}==0
.toString()或.valueOf(),轉為原生值,再去跟數字作對比:
截圖自MDN
試試把物件轉成原生值:
一頭霧水...所以它現在基本值是什麼?
我們比較一下:
這裏我們發現用valueOf()去轉型後,它是直接返回原本的空物件,它根本沒有轉型,所以我們可以略過這方法,我們直接看toString()的方法。{}可以被轉為"[object Object]"這個字串。
根據比較規則,如果是字串比較數字,即是這裏的"[object Object]" == 0,我們就要把"[object Object]"轉為數字:

NaN == 0,結果是false,所以{}==!{}是false
這一條問題最後部分的推斷是自己參考資料後再嘗試的方法,如有錯誤請不吝指教~~
[]==[]回傳false,因為兩個物件的地址不同。{}=={}回傳false,原因同上。[]==![]回傳true,因為會先處理![],變成[]==false,再變成[]==0。{}==!{}回傳false,因為會先處理!{},變成{}==false,再變成{}==0。當物件與數值進行比較時,會嘗試用toString()或valueOf()將物件轉型,因為valueOf()只會返回原本物件,所以用toString()把它轉成字串,再比較"[object Object]" == 0,回傳false。JavaScript 中的相等操作符 ( 详解 [] == []、[] == ![]、{} == !{} )
JavaScript 深入了解基本类型和引用类型的值
JavaScript values: not everything is an object
JAVASCRIPT.INFO - Object to primitive conversion